#include "Thread.hpp"
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <functional>
#include "LockGuard.hpp"
#include "log.hpp"

using std::bind;
using std::queue;
using std::string;
using std::vector;

const static int defaultnum = 3;

template <class T>
class ThreadPool
{
private:
    int _threadnum;   // 线程个数
    int _waitnum = 0; // 等在条件变量下的线程数目

    vector<Thread> _threads; // 管理所有的线程
    queue<T> _task_queue;    // 放任务的空间

    pthread_mutex_t _mutex; // 锁
    pthread_cond_t _cond;   // 条件变量

    bool _isrunning = false; // 线程创建成功了，运行了 or 线程创建失败

    // 添加单例模式
    static ThreadPool<T> *_instance; // 外部唯一可以使用的一个对象s
    static pthread_mutex_t _lock;

    // 构造函数私有
    ThreadPool(int threadnum = defaultnum)
        : _threadnum(threadnum)
    {
        pthread_mutex_init(&_mutex, nullptr); // 锁和条件变量的初始化
        pthread_cond_init(&_cond, nullptr);
        LOG(INFO, "ThreadPool construct()");
    }
    // 复制，拷贝都禁用
    ThreadPool(const ThreadPool &) = delete;
    ThreadPool &operator=(const ThreadPool &) = delete;

public:
    static ThreadPool<T> *GetInstance() // 给外部提供一个函数，可以获取到这个单例
    {
        if (_instance == nullptr)
        {
            LockGuard lockguard(&_lock);
            _instance = new ThreadPool();
            _instance->Init();
            _instance->Start();
            LOG(DEBUG, "创建线程池单例");
        }
        // else
        // {
        //     LOG(DEBUG, "获取线程池单例");
        // }
        return _instance;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex); // 锁和条件变量的销毁
        pthread_cond_destroy(&_cond);
    }

    // 上锁
    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    }
    // 解锁
    void Unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }
    // 条件变量——等待
    void Wait_cond()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    // 条件变量——唤醒一个线程
    void WakeUp_cond()
    {
        pthread_cond_signal(&_cond); // 向一个线程发信号，唤醒一个线程
    }
    // 条件变量——唤醒所有线程
    void WakeUpAll_cond()
    {
        pthread_cond_broadcast(&_cond); // 向所有线程发广播，唤醒所有线程
    }

    // 向任务队列中放任务
    bool Enqueue(const T &in)
    {
        if (!_isrunning) // 线程池里，有线程启动了，才能放任务
            return false;

        Lock();
        _task_queue.push(in);
        Unlock();

        if (_waitnum > 0) // 如果有等待的线程就去唤醒
            WakeUp_cond();

        return true;
    }
    // 从队列中拿到一个任务，然后处理，然后再去拿任务，处理...
    void Get_Handle_Task(const string &name)
    {
        LOG(INFO, "%s is running...", name.c_str());
        while (true)
        {
            // 去拿数据（任务）
            Lock();

            // 线程池退出 && 任务队列为空 ==> 线程退出结束
            if (!_isrunning && _task_queue.empty())
            {
                Unlock();
                break;
            }
            // 线程池没退出 && 任务队列为空 ==> 线程去等待
            while (_isrunning && _task_queue.empty())
            {
                _waitnum++;
                Wait_cond();
                _waitnum--;
            }
            // 线程池没退出 && 任务队列不为空 ==> 线程去执行任务
            // 线程池退出 &&任务队列不为空 ==> 线程去执行任务
            T t = _task_queue.front();
            _task_queue.pop();

            Unlock();

            // 处理数据（任务）
            LOG(DEBUG, "%s get a task", name.c_str());
            t();
            // 这里不在加锁区域了，但每个线程执行的 t() 都是不同的。
            // 因为在加锁区域中一个线程拿到一个 t 后，任务队列就会 pop()这个 t，而下一个线程拿到的就是下一个 front 元素了
            LOG(DEBUG, "%s handler a task,result is: %s", name.c_str(), t.ResultToString());
        }
    }

    // 构建所有线程对象放进 vector 中，但并不启动
    void Init()
    {
        for (int num = 0; num < _threadnum; num++)
        {
            string name = "thread-" + to_string(num);
            _threads.emplace_back(std::bind(&ThreadPool::Get_Handle_Task, this, placeholders::_1), name);
            LOG(INFO, "init thread %s done", name.c_str());
        }
        _isrunning = true; // 线程对象创建成功了
    }

    // 让 vector 中放的所有的线程都启动（这里内部的线程才是真的开始创建出来）
    void Start()
    {
        for (auto &thread : _threads)
        {
            thread.start();
        }
    }
    void Stop() // 终止线程池
    {
        Lock();

        _isrunning = false;
        WakeUpAll_cond(); // 唤醒所有线程
        // 因为这里是让线程池停止了，我们要回收所有线程了，要能 join 到所有线程，需要线程执行完它自己的线程函数了
        // 而线程函数中是有让线程去等待的操作的，所以当线程在条件下等待时，它是还算在执行它的线程函数的，
        // 所以要把它唤醒，让它跳出循环（线程函数中有循环），将函数执行完，这样就能回收了

        Unlock();
    }

    // 等待所有线程
    void Join()
    {
        if (_isrunning)
        {
            for (auto &thread : _threads)
            {
                thread.join();
                LOG(INFO, "%s is quit...", thread.name().c_str());
            }
        }
    }
};

// 静态成员在外面初始化
template <class T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;